package cscalendar.classes;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Calendar;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * CSCalendarRenderer menampilkan sebuah calendar ke layar sesuai dengan bulan
 * dan tahun display kalender
 *
 * @author Muqtafi Akhmad
 */
public class CSCalendarRenderer {

    /**
     * horizontal strip
     */
    private static char H_STRIP = '-';
    /**
     * horizontal cell
     */
    private static String H_CELL = "-------";
    /**
     * vertical strip
     */
    private static char V_STRIP = '|';
    /**
     * key untuk view previous month
     */
    private static int LEFT_KEY = 97;
    /**
     * key untuk view next month
     */
    private static int RIGHT_KEY = 100;
    /**
     * key untuk keluar mode view
     */
    private static int ESC_KEY = 27;

    /**
     * mengisikan reference calendar
     *
     * @param calendar calendar
     */
    public void setCalendar(CSCalendar calendar) {
        this.calendar = calendar;
    }

    /**
     * mengisikan bulan display
     *
     * @param currentMonth bulan display
     */
    public void setCurrentMonth(int currentMonth) {
        this.currentMonth = currentMonth;
    }

    /**
     * mengisikan tahun display
     *
     * @param currentYear tahun display
     */
    public void setCurrentYear(int currentYear) {
        this.currentYear = currentYear;
    }

    /**
     * mengembalikan reference ke calendar
     *
     * @return reference ke calendar
     */
    public CSCalendar getCalendar() {
        return calendar;
    }

    /**
     * mengembalikan bulan display
     *
     * @return bulan display
     */
    public int getCurrentMonth() {
        return currentMonth;
    }

    /**
     * mengembalikan tahun display
     *
     * @return tahun display
     */
    public int getCurrentYear() {
        return currentYear;
    }
    /**
     * reference ke calendar yang ditampilkan
     */
    private CSCalendar calendar;
    /**
     * bulan display
     */
    private int currentMonth;
    /**
     * tahun display
     */
    private int currentYear;
    /**
     * nilai bulan default
     */
    private static int DEFAULT_MONTH = 1;
    /**
     * nilai tahun default
     */
    private static int DEFAULT_YEAR = 1990;

    /**
     * konstruktor dari CSCalendarRenderer dengan parameter calendar
     *
     * @param calendar reference ke calendar yang didisplay
     */
    public CSCalendarRenderer(CSCalendar calendar) {
        this.calendar = calendar;
        if (calendar.getEvents().size() > 0) {
            CSEvent firstEvent = calendar.getEvents().peek();
            currentMonth = firstEvent.getDate().get(Calendar.MONTH) + 1;
            currentYear = firstEvent.getDate().get(Calendar.YEAR);
        } else {
            currentMonth = DEFAULT_MONTH;
            currentYear = DEFAULT_YEAR;
        }
    }

    /**
     * konstruktor CSCalendarRenderer dengan parameter calendar, display month,
     * dan display year
     *
     * @param calendar reference ke calendar
     * @param currentMonth display month
     * @param currentYear display year
     */
    public CSCalendarRenderer(CSCalendar calendar, int currentMonth, int currentYear) {
        this.calendar = calendar;
        this.currentMonth = currentMonth;
        this.currentYear = currentYear;
    }

    /**
     * copy constructor dari CSCalendarRenderer
     *
     * @param anotherCalendarRenderer renderer lain
     */
    public CSCalendarRenderer(CSCalendarRenderer anotherCalendarRenderer) {
        this.calendar = (CSCalendar) anotherCalendarRenderer.getCalendar().elementClone();
        this.currentMonth = anotherCalendarRenderer.getCurrentMonth();
        this.currentYear = anotherCalendarRenderer.getCurrentYear();
    }

    /**
     * memajukan display month
     */
    void advanceMonth() {
        currentMonth++;
        if (currentMonth == 13) {
            currentMonth = 1;
            currentYear++;
        }
    }

    /**
     * memundurkan display month
     */
    void backMonth() {
        currentMonth--;
        if (currentMonth == 0) {
            currentMonth = 12;
            currentYear--;
        }
    }

    /**
     * menampilkan calendar
     */
    void renderCalendar() {
        int iter, dayIter, dayMax = CSCalendarHelper.maxDaysInMonth(currentMonth, currentYear);
        int dayHeader = 0;
        int currDay = CSCalendarHelper.getFirstDay(currentYear, currentMonth);
        String dayString[] = {"Ahad  ", "Senin ", "Selasa", "Rabu  ", "Kamis ", "Jumat ", "Sabtu ", "Undef "};

        currDay %= 7;
        System.out.println("Month " + currentMonth + ", Year " + currentYear);
        endLine();
        System.out.format("Press '%c' to next month, '%c' to previous month, and '%c' to exit\n", RIGHT_KEY, LEFT_KEY, ESC_KEY);
        // end of print header, start of print first row
        endLine();
        printHLine();
        endLine();
        System.out.print(V_STRIP);
        for (iter = 0; iter < CSCalendarHelper.DAY_IN_WEEK_MAX; iter++) {
            System.out.print(dayString[iter] + V_STRIP); // print day
            dayHeader++;
        }
        endLine();
        printHLine();
        // end of print first row
        endLine();
        System.out.print(V_STRIP);
        for (iter = 0; iter < currDay; iter++) {
            System.out.print("      " + V_STRIP); // start filler
        }
        for (dayIter = 1; dayIter <= dayMax; dayIter++) {
            int eventCount = calendar.getEventCountInDay(currentYear, currentMonth, dayIter);
            if (eventCount > 0) {
                System.out.format("%02d.%03d%c", dayIter, eventCount, V_STRIP); // prints count of event
            } else {
                System.out.format("%02d.   %c", dayIter, V_STRIP); // prints nothing if there is no event
            }
            currDay++;
            if (currDay == CSCalendarHelper.DAY_IN_WEEK_MAX) {
                currDay -= CSCalendarHelper.DAY_IN_WEEK_MAX; // reset day 
                endLine();
                printHLine(); // print new horizontal line
                endLine();
                if (dayIter < dayMax) {
                    System.out.print(V_STRIP);
                }
            }
        }
        if (currDay != 0) {
            for (iter = currDay; iter < CSCalendarHelper.DAY_IN_WEEK_MAX; iter++) {
                System.out.print("      " + V_STRIP); // end filler
            }
            endLine();
            printHLine();
        }
        endLine();
    }

    /**
     * Print end line
     */
    private void endLine() {
        System.out.println();
    }

    /**
     * Print horizontal line
     */
    private void printHLine() {
        int iter;
        System.out.print(H_STRIP);
        for (iter = 0; iter < CSCalendarHelper.DAY_IN_WEEK_MAX; iter++) {
            System.out.print(H_CELL);
        }
    }

    /**
     * menampilkan calendar ke dalam mode continuous display, di mana calendar
     * dapat berpindah ke next dan previous month
     *
     * @param args arguments berisi display month dan year
     */
    void showCalendar(String args[]) {
        if (args.length == 4) {
            if (calendar.getEvents().size() == 0) {
                System.out.println("The calendar is empty");
            } else {
                int readChar;
                currentYear = Integer.parseInt(args[2]); // set year
                currentMonth = Integer.parseInt(args[3]); // set month
                BufferedReader inputStream = new BufferedReader(new InputStreamReader(System.in));
                try {
                    do {
                        renderCalendar();
                        readChar = inputStream.read();
                        if (readChar == RIGHT_KEY) {
                            advanceMonth();
                        } else if (readChar == LEFT_KEY) {
                            backMonth();
                        }
                    } while (readChar != ESC_KEY);
                } catch (IOException ex) {
                    Logger.getLogger(CSCalendarRenderer.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }
}
